#pragma once
#include "lib.h"

template <typename T>
class Vec
{
public:
	Vec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
	Vec(initializer_list<T> il) {
		auto newdata = alloc_n_copy(il.begin(), il.end());
		elements = newdata.first;
		first_free = cap = newdata.second;
	}
	//move constructor
	Vec(const Vec &s) {
		auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
		elemenst = newdata.first;
		first_free = cap = newdata.second;
	}
	Vec(Vec &&s) noexcept 
	:elements(s.elements), first_free(s.first_free),cap(s.cap){
		s.elements = s.first_free = s.cap = nullptr;
	}
	Vec& operator=(const Vec &rhs) {
		auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
		free();
		elemenst = newdata.first;
		first_free = cap = newdata.second;
		return *this;
	}
	Vec& operator=(Vec &&rhs) noexcept {
		if (this != &rhs) {
			free();
			elements = rhs.elements;
			first_free = rhs.first_free;
			cap = rhs.cap;
			rhs.elements = rhs.first_free = rhs.cap = nullptr;
		}
		return *this;
	}

	~Vec() {}

	void push_back(const T &c) {
		chk_n_alloc();
		alloc.construct(first_free++, c);
	}
	size_t size() const { return first_free - elements; }
	size_t capacity() const { return cap - elements; }
	T* begin() { return elements; }
	T* end() { return first_free; }

	void reserve(size_t n) {
		if (n > capacity()) {
			alloc_n_move(n);
		}
	}
	void resize(size_t n) {
		resize(n, T());
	}
	void resize(size_t n, const T& c) {
		if (n > size()) {
			if (n > capacity())
				reserve(n * 2);
			for (size_t i = size(); i != n; ++i) {
				alloc.construct(first_free++, c);
			}
		} else if (n < size()) {
			while (first_free != elements + n) {
				alloc.destroy(--first_free);
			}
		}
	}

private:
	static allocator<T> alloc;
	void chk_n_alloc() { if (size() == capacity()) reallocate(); }
	void alloc_n_move(size_t n) {
		auto newdata = alloc.allocate(n);
		auto dest = newdata;
		auto elem = elements;
		for (size_t i = 0; i != size(); ++i) {
			alloc.construct(dest++, move(*elem++));
		}
		free();
		elements = newdata;
		first_free = dest;
		cap = elements + n;
	}
	pair<T*, T*> alloc_n_copy(const T* beg, const T* end) {
		auto data = alloc.allocate(end - beg);
		return{ data,uninitialized_copy(beg,end,data) };
	}
	void free() {
		if (elements) {
			for_each(elements, first_free,
				[this](T &rhs) {alloc.destroy(&rhs); });
			alloc.deallocate(elements, cap - elements);
		}
	}
	void reallocate() {
		auto newcapacity = size() ? 2 * size() : 1;
		alloc_n_move(newcapacity);
	}


	T *elements;
	T *first_free;
	T *cap;
};